﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using System.ComponentModel;

using Microsoft.Practices.Unity;
using Microsoft.Win32;

using ObjectEditor;
using ObjectEditor.Display3D;
using ObjectEditor.Editor;

using EQEmu.Grids;
using EQEmu.Grids.Display3D;
using EQEmu.Database;

namespace EQEmu.GridsPlugin
{
    internal class GridsVisual3D : ModelVisual3D
    {
    }

    public class GridsRibbonTabViewModel : ViewModel, IWorldModel3DProvider, IObjectEditor, IDisposable, IMessenger
    {
        public event EventHandler<ModelChangedEventArgs> ModelChanged;
        public event EventHandler<CameraChangeEventArgs> RequestCameraChange;
        public event EventHandler<ObjectSelectedEventArgs> ObjectSelected;
        public event EventHandler<MessageEventArgs> MessageSent;

        public const string GridSelect = "GRID.SELECT";

        private Transform3D _transform;

        private string _zoneShortName;
        private int _zoneId;
        private ZoneGrids _zoneGrids;
        private GridsDisplay3D _display3d = new GridsDisplay3D();
        private ModelVisual3D _modelVisual;
        private double _zAdjustment;
        private bool _useDatabase;
        private Grid _selectedGrid;
        private grid_entries _selectedWaypoint;
        private IEnumerable<grid_entries> _multipleSelections = null;

        private DelegateCommand _saveFileCommand;
        private DelegateCommand _openFileCommand;
        private DelegateCommand _searchZoneCommand;
        private DelegateCommand _removeGridCommand;
        private DelegateCommand _addGridCommand;
        private DelegateCommand _commitChangesCommand;
        private DelegateCommand _createWaypointCommand;
        private DelegateCommand _removeWaypointCommand;

        public GridsRibbonTabViewModel()
        {
            ZAdjustment = 2.0;
        }

        [Dependency]
        public EQEmuContextFactory EntitiesFactory
        {
            get;
            set;
        }

        public IEnumerable<Grid> Grids
        {
            get
            {
                if (_zoneGrids != null)
                {
                    return _zoneGrids.Grids;
                }
                else return null;
            }
        }
        
        public DelegateCommand CreateWaypointCommand
        {
            get
            {
                if (_createWaypointCommand == null)
                {
                    _createWaypointCommand = new DelegateCommand(
                        x =>
                        {
                            var wp = SelectedGrid.CreateEntry();
                            SelectedGrid.AddEntry(wp);
                            SelectedWaypoint = wp;
                        },
                        y =>
                        {
                            return SelectedGrid != null;
                        });
                }
                return _createWaypointCommand;
            }
        }

        public DelegateCommand RemoveWaypointCommand
        {
            get
            {
                if (_removeWaypointCommand == null)
                {
                    _removeWaypointCommand = new DelegateCommand(
                        x =>
                        {
                            SelectedGrid.RemoveEntry(SelectedWaypoint);

                            if (_multipleSelections != null)
                            {
                                foreach (var wp in _multipleSelections)
                                {
                                    SelectedGrid.RemoveEntry(wp);
                                }
                            }

                            SelectedWaypoint = null;
                        },
                        y =>
                        {
                            return SelectedWaypoint != null && SelectedGrid != null;
                        });
                }
                return _removeWaypointCommand;
            }
        }

        public DelegateCommand CommitChangesCommand
        {
            get
            {
                if (_commitChangesCommand == null)
                {
                    _commitChangesCommand = new DelegateCommand(
                        x =>
                        {
                        },
                        y =>
                        {
                            return (_zoneGrids as ZoneGridsDatabase ) != null;
                        });
                }
                return _commitChangesCommand;
            }
        }

        public DelegateCommand CreateGridCommand
        {
            get
            {
                if (_addGridCommand == null)
                {
                    _addGridCommand = new DelegateCommand(
                        x =>
                        {
                            var grid = _zoneGrids.CreateGrid();
                            _zoneGrids.AddGrid(grid);
                            SelectedGrid = grid;
                        },
                        y =>
                        {
                            return _zoneGrids != null;
                        });
                }
                return _addGridCommand;
            }
        }

        public DelegateCommand RemoveGridCommand
        {
            get
            {
                if (_removeGridCommand == null)
                {
                    _removeGridCommand = new DelegateCommand(
                        x =>
                        {
                            if (SelectedGrid != null)
                            {
                                _zoneGrids.RemoveGrid(SelectedGrid);
                            }
                            SelectedGrid = null;
                        },
                        y =>
                        {
                            return _zoneGrids != null && SelectedGrid != null;
                        });
                }
                return _removeGridCommand;
            }
        }

        public DelegateCommand SaveFileCommand
        {
            get
            {
                if (_saveFileCommand == null)
                {
                    _saveFileCommand = new DelegateCommand(
                        x =>
                        {
                        },
                        y =>
                        {
                            return _zoneGrids != null;
                        });
                }
                return _saveFileCommand;
            }
        }

        public DelegateCommand OpenFileCommand
        {
            get
            {
                if (_openFileCommand == null)
                {
                    _openFileCommand = new DelegateCommand(
                        x =>
                        {
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _openFileCommand;
            }
        }

        public DelegateCommand SearchZoneCommand
        {
            get
            {
                if (_searchZoneCommand == null)
                {
                    _searchZoneCommand = new DelegateCommand(
                        x =>
                        {
                            LoadZoneData();
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _searchZoneCommand;
            }
        }

        private ZoneGrids ZoneGrids
        {
            get { return _zoneGrids; }
            set
            {
                if (value != null)
                {
                    if (_zoneGrids != null)
                    {
                        _zoneGrids.GridCollectionChanged -= _zoneGrids_GridCollectionChanged;
                        var disposer = _zoneGrids as IDisposable;
                        if (disposer != null) disposer.Dispose();
                    }

                    _zoneGrids = value;
                    _zoneGrids.GridCollectionChanged += _zoneGrids_GridCollectionChanged;
                    SaveFileCommand.RaiseCanExecuteChanged();
                    _display3d = new GridsDisplay3D();

                    Model3D = new GridsVisual3D()
                    {
                        Content = _display3d.Model,
                        Transform = Transform3D
                    };
                }
                CreateGridCommand.RaiseCanExecuteChanged();
                CommitChangesCommand.RaiseCanExecuteChanged();
                OnPropertyChanged("Grids");
            }
        }

        void _zoneGrids_GridCollectionChanged(object sender, GridCollectionChangedEventArgs e)
        {
            OnPropertyChanged("Grids");
        }

        public bool UseDatabase
        {
            get { return _useDatabase; }
            set
            {
                _useDatabase = value;
                OnPropertyChanged("UseDatabase");
            }
        }
        
        private void LoadZoneData()
        {
            if (_zoneGrids != null) // && _zoneGrids.H _zoneDoors.HasModifications)
            {
                var result =
                    System.Windows.MessageBox.Show("Discard all changes?", "Discard channges", System.Windows.MessageBoxButton.YesNo);
                if (result == System.Windows.MessageBoxResult.No) return;
            }

            if (UseDatabase)
            {
                try
                {
                    ZoneGrids = ZoneGridsDatabase.Create(ZoneShortName, EntitiesFactory);
                }
                catch (Exception e)
                {
                    System.Windows.MessageBox.Show("Could not created object:" + e.Message + Environment.NewLine + e.StackTrace);
                }
            }
            else
            {
                ZoneGrids = new ZoneGrids(ZoneId);
            }
        }
        
        public string ZoneShortName
        {
            get { return _zoneShortName; }
            set
            {
                _zoneShortName = value;
                OnPropertyChanged("ZoneShortName");
            }
        }

        public int ZoneId
        {
            get { return _zoneId; }
            set
            {
                _zoneId = value;
                OnPropertyChanged("ZoneId");
            }
        }

        public double ZAdjustment
        {
            get { return _zAdjustment; }
            set
            {
                _zAdjustment = value;
                OnPropertyChanged("ZAdjustment");
            }
        }

        public Grid SelectedGrid
        {
            get { return _selectedGrid; }
            set
            {
                if (_selectedGrid != null)
                {
                    _selectedGrid.GridEntriesChanged -= _selectedGrid_GridEntriesChanged;
                }

                _selectedGrid = value;               

                OnPropertyChanged("SelectedGrid");
                if (value != null)
                {
                    _selectedGrid.GridEntriesChanged += _selectedGrid_GridEntriesChanged;
                    OnObjectSelected(value.GridValue,null);
                    _display3d.ShowGrid(value);
                }
                else
                {
                    OnObjectSelected(null, null);
                }                
                RemoveGridCommand.RaiseCanExecuteChanged();
                CreateWaypointCommand.RaiseCanExecuteChanged(); 
                OnPropertyChanged("GridWaypoints");                
            }
        }

        public grid_entries SelectedWaypoint
        {
            get
            {
                return _selectedWaypoint;
            }
            set
            {
                if (_selectedWaypoint != null)
                {
                    _selectedWaypoint.PropertyChanged -= _selectedWaypoint_PropertyChanged;
                }

                _selectedWaypoint = value;
                if (_selectedWaypoint != null)
                {
                    _selectedWaypoint.PropertyChanged += _selectedWaypoint_PropertyChanged;

                    var look = _transform.Transform(new Point3D(_selectedWaypoint.x, _selectedWaypoint.y, _selectedWaypoint.z));
                    OnCameraChange(new CameraChangeEventArgs()
                    {                        
                        LookAtPosition = look
                    });
                }

                OnObjectSelected(value, _multipleSelections);
                OnPropertyChanged("SelectedWaypoint");

                if (SelectedGrid != null)
                {
                    _display3d.ShowGrid(SelectedGrid);
                }

                _removeWaypointCommand.RaiseCanExecuteChanged();
            }
        }

        void _selectedWaypoint_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (SelectedGrid != null)
            {
                _display3d.ShowGrid(SelectedGrid);
            }
        }

        public IEnumerable<grid_entries> GridWaypoints
        {
            get
            {
                if (_selectedGrid != null)
                {
                    return _selectedGrid.Entries;
                }
                else return null;
            }
        }

        void _selectedGrid_GridEntriesChanged(object sender, GridEntriesChangedEventArgs e)
        {
            OnPropertyChanged("GridWaypoints");
        }

        [Dependency("EQEmu.Transform")]
        public Transform3D Transform3D
        {
            get { return _transform; }
            set
            {
                _transform = value;
            }
        }

        public ModelVisual3D Model3D
        {
            get { return _modelVisual; }
            private set
            {
                if (_modelVisual != null)
                {
                    _modelVisual.Children.Clear();
                }

                _modelVisual = value;
                OnPropertyChanged("Model3D");
                OnModelChanged();
            }        
        }

        protected void OnModelChanged()
        {
            var e = ModelChanged;
            if (e != null)
            {
                e(this, new ModelChangedEventArgs());
            }
        }

        protected void OnObjectSelected(INotifyPropertyChanged obj,IEnumerable<INotifyPropertyChanged> objs)
        {
            var e = ObjectSelected;
            if (e != null)
            {
                e(this, new ObjectSelectedEventArgs(obj,objs));
            }
        }

        protected void OnCameraChange(CameraChangeEventArgs change)
        {
            var e = RequestCameraChange;
            if (e != null)
            {
                e(this, change);
            }
        }        

        public void UserClicked(System.Windows.Media.Media3D.Point3D point, AppInformation info)
        {
            if (info.SelectedRibbonTab == null || (info.SelectedRibbonTab as GridsRibbonTab) == null) return;
            if (_zoneGrids == null || SelectedGrid == null) return;

            var p = Transform3D.Transform(point);

            if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) && SelectedWaypoint != null)
            {
                var wp = SelectedWaypoint;
                wp.x = (float)p.X;
                wp.y = (float)p.Y;
                wp.z = (float)(p.Z + ZAdjustment);

            }
            else if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && SelectedWaypoint != null)
            {
                //change the heading
                SelectedWaypoint.LookAt(p.X, p.Y, p.Z);
                if (_multipleSelections != null)
                {
                    foreach (var wp in _multipleSelections)
                    {
                        wp.LookAt(p.X, p.Y, p.Z);
                    }
                }
            }
            else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) )
            {
                var wp = SelectedGrid.CreateEntry();
                wp.x = (float)p.X;
                wp.y = (float)p.Y;
                wp.z = (float)p.Z;
                SelectedGrid.AddEntry(wp);
                SelectedWaypoint = wp;
            }
            else
            {
                var wp = SelectedGrid.NearbyWaypoint(p);

                if (info.World3DSelectionBox != null)
                {
                    var wps = new List<grid_entries>();
                    foreach (var w in SelectedGrid.Entries)
                    {
                        var tp = new Point3D(w.x, w.y, w.z);
                        tp = Transform3D.Transform(tp);
                        if (info.World3DSelectionBox(tp, 2.0))
                        {
                            wps.Add(w);
                        }
                    }
                    _multipleSelections = wps;
                    if (wp == null && _multipleSelections.Count() > 0)
                    {
                        wp = _multipleSelections.ElementAt(0);
                    }
                }

                if (wp != null)
                {
                    SelectedWaypoint = wp;
                }
            }
        }

        public void ConstrainView(ViewConstraints constraints)
        {
            if (_display3d == null) return;

            _display3d.UpdateView(new EQEmu.Display3D.ViewConstraints()
            {
                MaxX = constraints.MaxX,
                MaxY = constraints.MaxY,
                MaxZ = constraints.MaxZ,
                MinX = constraints.MinX,
                MinY = constraints.MinY,
                MinZ = constraints.MinZ
            });
        }

        protected virtual void Dispose(bool disposeAll)
        {
            if (_display3d != null)
            {
                _display3d.Dispose();
            }

            if (_zoneGrids != null)
            {
                var disposer = _zoneGrids as IDisposable;
                if (disposer != null) disposer.Dispose();
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }        

        public void MessageAvailable(object sender, MessageEventArgs data)
        {
            if (_zoneGrids == null) return;

            switch (data.Title)
            {
                case GridSelect:
                    var grid = _zoneGrids.Grids.FirstOrDefault(x => x.GridValue.id == (int)data.Data);
                    if (grid != null)
                    {
                        SelectedWaypoint = null;
                        SelectedGrid = grid;                        
                    }
                    break;
                default:
                    break;
            }
        }
    }
}
